﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Security.Tokens;
using System.IdentityModel.Tokens;
using System.IdentityModel.Selectors;
using Custom = Microsoft.Cryptography.WCF;

namespace Microsoft.Cryptography.WCF
{
    /// <summary>
    /// Represents a <see cref="System.IdentityModel.Selectors.SecurityTokenManager"/> implementation that provides security token 
    /// serializers based on the <see cref="System.ServiceModel.Description.ServiceCredentials"/> configured on the service. 
    /// This implementation uses the same logic than the base class (<see cref="System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager"/>) 
    /// except for security tokens of the type <see cref="SecurityTokenTypes.X509Certificate"/> and security token serializers of type 
    /// <see cref="System.ServiceModel.Security.WSSecurityTokenSerializer"/>. In these two cases this implementation returns customs classes, 
    /// which support RSA-Signatures with SHA-2 encryption in XML-Signatures
    /// </summary>
    public class ServiceCredentialsSecurityTokenManager :
        System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager
    {
        
        Custom.ServiceCredentials _credentials;

        /// <summary>
        /// Represents a <see cref="System.IdentityModel.Selectors.SecurityTokenManager"/> implementation that provides security token 
        /// serializers based on the <see cref="System.ServiceModel.Description.ServiceCredentials"/> configured on the service. 
        /// This implementation uses the same logic than the base class (<see cref="System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager"/>) 
        /// except for security tokens of the type <see cref="SecurityTokenTypes.X509Certificate"/> and security token serializers of type 
        /// <see cref="System.ServiceModel.Security.WSSecurityTokenSerializer"/>. In these two cases this implementation returns customs classes, 
        /// which support RSA-Signatures with SHA-2 encryption in XML-Signatures
        /// </summary>
        /// <param name="parent">The <see cref="System.ServiceModel.Description.ServiceCredentials"/></param>
        public ServiceCredentialsSecurityTokenManager(Custom.ServiceCredentials parent)
            : base(parent)
        {
            _credentials = parent;
        }

        /// <summary>
        /// Creates a security token serializer based on the security version passed in. If the securityTokenSerializer returned 
        /// from the base class (<see cref="System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager"/>) is of type 
        /// <see cref="System.ServiceModel.Security.WSSecurityTokenSerializer"/> a custom security token serializer of type 
        /// <see cref="Microsoft.Cryptography.WCF.WSSecurityTokenSerializer"/> will be returned.
        /// </summary>
        /// <param name="version">The <see cref="System.IdentityModel.Selectors.SecurityTokenVersion"/> of the security token.</param>
        /// <returns>Returns a security token serializer based on the security version passed in. If the securityTokenSerializer returned 
        /// from the base class (<see cref="System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager"/>) is of type 
        /// <see cref="System.ServiceModel.Security.WSSecurityTokenSerializer"/> a custom security token serializer of type 
        /// <see cref="Microsoft.Cryptography.WCF.WSSecurityTokenSerializer"/> will be returned.</returns>
        public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
        {
            SecurityTokenSerializer serializer = base.CreateSecurityTokenSerializer(version);

            if (serializer is System.ServiceModel.Security.WSSecurityTokenSerializer)
            {
                return new Microsoft.Cryptography.WCF.WSSecurityTokenSerializer((System.ServiceModel.Security.WSSecurityTokenSerializer)serializer);
            }
            return serializer;
        }
        
        /// <summary>
        /// Creates a security token provider based on the <see cref="System.IdentityModel.Selectors.SecurityTokenRequirement"/>. 
        /// If the TokenType of the <see cref="System.IdentityModel.Selectors.SecurityTokenRequirement"/> is <see cref="System.IdentityModel.Tokens.SecurityTokenTypes.X509Certificate"/>)
        /// a custom security token provider of type 
        /// <see cref="Microsoft.Cryptography.WCF.X509SecurityTokenProvider"/> will be returned.
        /// </summary>
        /// <param name="tokenRequirement">The <see cref="System.IdentityModel.Selectors.SecurityTokenRequirement"/></param>
        /// <returns> Creates a security token provider based on the <see cref="System.IdentityModel.Selectors.SecurityTokenRequirement"/>. 
        /// If the TokenType of the <see cref="System.IdentityModel.Selectors.SecurityTokenRequirement"/> is <see cref="System.IdentityModel.Tokens.SecurityTokenTypes.X509Certificate"/>)
        /// a custom security token provider of type 
        /// <see cref="Microsoft.Cryptography.WCF.X509SecurityTokenProvider"/> will be returned.</returns>
        public override System.IdentityModel.Selectors.SecurityTokenProvider CreateSecurityTokenProvider(System.IdentityModel.Selectors.SecurityTokenRequirement tokenRequirement)
        {
            SecurityTokenProvider result = null;

            if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
            {
                MessageDirection direction = tokenRequirement.GetProperty<MessageDirection>(ServiceModelSecurityTokenRequirement.MessageDirectionProperty);
                if (direction == MessageDirection.Output)
                {
                    if (tokenRequirement.KeyUsage == SecurityKeyUsage.Signature)
                    {
                        result = new Custom.X509SecurityTokenProvider(_credentials.ServiceCertificate.Certificate);
                    }
                }
                else
                {
                    if (tokenRequirement.KeyUsage == SecurityKeyUsage.Exchange)
                    {
                        result = new Custom.X509SecurityTokenProvider(_credentials.ServiceCertificate.Certificate);
                    }
                }
            }

            if (result == null)
            {
                result = base.CreateSecurityTokenProvider(tokenRequirement);
            }
            return result;
        }
    }
}
